using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Data.Linq;
using System.Linq;
using System.Linq.Dynamic;
using Tiffer;
using ABC = WebSupergoo.ABCpdf6;

////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Dimok
//
// summary:	.
////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Dimok {
	public class ABCPdf {

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill form. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="FormPath">				Full pathname of the form file. </param>
		/// <param name="Context">				The context. </param>
		/// <param name="CommandText">		The command text. </param>
		/// <param name="CommandParams">	Options for controlling the command. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static List<byte[]> FillForm(string FormPath, DataContext Context, string CommandText, params object[] CommandParams) {
			List<byte[]> pdfFiles = new List<byte[]>();
			var pdfDoc = new ABC.Doc();
			pdfDoc.Read(FormPath);
			var props = pdfDoc.Form.GetFieldNames().Select(f => new DynamicProperty(f, typeof(string)));
			Type type = DynamicExpression.CreateClass(props);
			var Records = Context.ExecuteQuery(type, CommandText, CommandParams);
			foreach (var r in Records) {
				if (pdfDoc.PageCount == 0) pdfDoc.Read(FormPath);
				foreach (var field in pdfDoc.Form.Fields.Cast<ABC.Objects.Field>()) {
					object v = null;
					if (r.TryGetProperty(field.Name, out v))
						field.Value = v + "";
				}
					//field.Value = type.GetProperty(field.Name).GetValue(r, null) + "";
				pdfDoc.Form.Stamp();
				pdfDoc.Flatten();
				pdfFiles.Add(pdfDoc.GetData());
				pdfDoc.Clear();
			}
			return pdfFiles;
		}

		#region PageCount

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Page count. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="fileName">	Filename of the file. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static int PageCount(string fileName) {
			return PageCount(FileManager.GetBytesFromFile(fileName));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Page count. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="file">	The file. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static int PageCount(byte[] file) {
			if (file == null || file.Length == 0) return 0;
			ABC.Doc doc = new ABC.Doc();
			try {
				doc.Read(file);
				return doc.PageCount;
			} finally {
				doc.Clear();
			}
		} 
		#endregion

		#region GetPages

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the pages. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="fileName">			Filename of the file. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	The pages. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[] GetPages(string fileName,params int[] pageNumbers) {
			return GetPages(FileManager.GetBytesFromFile(fileName), pageNumbers);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the pages. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="file">					The file. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	The pages. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[] GetPages(byte[] file,params int[] pageNumbers) {
			byte[][] pagesIn = Slice(file);
			List<byte[]> pagesOut = new List<byte[]>();
			foreach (int pageNumber in FileManager.MakePageNumbers(pagesIn.Length, pageNumbers))
				pagesOut.Add(pagesIn[pageNumber - 1]);
			return CombinePDF(pagesOut.ToArray());
		} 
		#endregion

		#region MovePages

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Move pages. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="file">					The file. </param>
		/// <param name="pageAfter">		The page after. </param>
		/// <param name="copy">					true to copy. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

        public static void MovePages(string fileName, int pageAfter, bool copy, params int[] pageNumbers)
        {
            var bytesOut = MovePages(FileManager.GetBytesFromFile(fileName), pageAfter, copy, pageNumbers);
            FileManager.GetFileFromBytes(bytesOut,fileName);
        }
		public static byte[] MovePages(byte[] file, int pageAfter, bool copy, params int[] pageNumbers) {
			pageNumbers = FileManager.MakePageNumbers(PageCount(file), pageNumbers);
			if (!copy) {
				int pageAdjust = 0;
				foreach (int page in pageNumbers)
					if (page < pageAfter) pageAdjust++;
				pageAfter -= pageAdjust;
			}
			List<byte[]> lstMove = new List<byte[]>(Slice(file, pageNumbers));
			int[] pageNumbersInv = copy ? new int[]{} : FileManager.MakePageNumbersInverted(PageCount(file), pageNumbers);
			List<byte[]> lstTo = new List<byte[]>(Slice(file, pageNumbersInv));
			lstTo.InsertRange(pageAfter, lstMove);
			return CombinePDF(lstTo.ToArray());
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Move pages. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="fileFrom">			The file from. </param>
		/// <param name="fileTo">				The file to. </param>
		/// <param name="pageAfter">		The page after. </param>
		/// <param name="copy">					true to copy. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[] MovePages(byte[] fileFrom, byte[] fileTo, int pageAfter, bool copy, params int[] pageNumbers) {
			pageNumbers = FileManager.MakePageNumbers(PageCount(fileFrom), pageNumbers);
			List<byte[]> lstMove = new List<byte[]>(Slice(fileFrom, pageNumbers));
			List<byte[]> lstTo = new List<byte[]>(Slice(fileTo));
			lstTo.InsertRange(pageAfter, lstMove);
			return CombinePDF(lstTo.ToArray());
		}
		#endregion

		#region AddPages

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Adds the pages. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="fileFrom">			The file from. </param>
		/// <param name="fileTo">				The file to. </param>
		/// <param name="pageAfter">		The page after. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[] AddPages(byte[] fileFrom, byte[] fileTo, int pageAfter, params int[] pageNumbers) {
			List<byte[]> lstTo = new List<byte[]>(ABCPdf.Slice(fileTo));
			lstTo.InsertRange(pageAfter, ABCPdf.Slice(fileFrom, FileManager.MakePageNumbers(PageCount(fileFrom), pageNumbers)));
			return ABCPdf.CombinePDF(lstTo.ToArray());
		}
		#endregion

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Tiff to pdf. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="file">	The file. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[] TiffToPDF(byte[] file) {
			ABC.Doc docAll = new ABC.Doc();
			Bitmap pic = new Bitmap(new MemoryStream(file));
			int pageCount = pic.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
			for (int page = 0; page < pageCount; page++) {
				try {
					pic.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, page);
				} catch { continue; }
				ABC.Doc doc = new ABC.Doc();
				doc = new ABC.Doc();
				int w = pic.Size.Width;// doc.GetInfoInt(pageID, "Width");
				int h = pic.Size.Height;// doc.GetInfoInt(pageID, "Height");
				doc.Rect.String = "0 0 " + w + " " + Convert.ToInt32(h * pic.HorizontalResolution / pic.VerticalResolution);
				doc.Rendering.DotsPerInchX = pic.HorizontalResolution;
				doc.Rendering.DotsPerInchY = pic.VerticalResolution;
				doc.MediaBox.String = doc.Rect.String;
				MemoryStream msFile = new MemoryStream();
				pic.Save(msFile,Tiff.GetTiffEncoderInfo(),InitEncodeParams());
				int pageID = doc.AddImageData(msFile.ToArray(), 1);
				msFile.Close();
				docAll.Append(doc);
				doc.Clear();
			}
			try {
				return docAll.GetData();
			} finally {
				docAll.Clear();
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initialises the encode parameters. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static EncoderParameters InitEncodeParams() {
			EncoderParameters eps = new EncoderParameters(1);
			//eps.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
			eps.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
			//GetCompressionParam(bitMap.GetEncoderParameterList(GetTiffEncoderInfo().Clsid)).;
			//encodeParams.Param[0] = GetCompressionParam(bitMap.GetEncoderParameterList(GetTiffEncoderInfo().Clsid));
			return eps;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Combine pdf. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <exception cref="Exception">	Thrown when exception. </exception>
		///
		/// <param name="files">	The files. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[] CombinePDF(byte[][] files) {
			ABC.Doc docAll = null;
			try {
				docAll = new ABC.Doc();
			} catch {
				try {
					docAll = new ABC.Doc();
				} catch (Exception exc) {
					throw;
				}
			}
			foreach (byte[] file in files) {
				ABC.Doc doc = new ABC.Doc();
				try {
					doc.Read(file);
				} catch (Exception exc) {
					doc = new ABC.Doc();
					Bitmap pic = new Bitmap(new MemoryStream(file));
					int pageCount = pic.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
					for (int page = 0; page < pageCount; page++) {
						pic.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, page);
					}
					int w = pic.Size.Width;// doc.GetInfoInt(pageID, "Width");
					int h = pic.Size.Height;// doc.GetInfoInt(pageID, "Height");
					doc.Rect.String = "0 0 " + w + " " + Convert.ToInt32(h * pic.HorizontalResolution / pic.VerticalResolution);
					doc.Rendering.DotsPerInchX = pic.HorizontalResolution;
					doc.Rendering.DotsPerInchY = pic.VerticalResolution;
					doc.MediaBox.String = doc.Rect.String;
					MemoryStream msFile = new MemoryStream();
					pic.Save(msFile, Tiff.GetTiffEncoderInfo(), InitEncodeParams());
					int pageID = doc.AddImageData(msFile.ToArray(), 1);
					msFile.Close();
				}
				docAll.Append(doc);
				doc.Clear();
			}
			try {
				return docAll.GetData();
			} finally {
				docAll.Clear();
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Deletes the pages. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="file">					The file. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[] DeletePages(byte[] file, params int[] pageNumbers) {
			return CombinePDF(Slice(file, FileManager.MakePageNumbersInverted(PageCount(file), pageNumbers)));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Slices the given file. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="fileName">			Filename of the file. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[][] Slice(string fileName, params int[] pageNumbers) {
			return Slice(FileManager.GetBytesFromFile(fileName),pageNumbers);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Slices the given file. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		///
		/// <param name="file">					The file. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static byte[][] Slice(byte[] file,params int[] pageNumbers) {
			if (file == null || file.Length == 0) return new byte[][] { };
			if (file.Length == 1) return new byte[][] { file };
			ABC.Doc docFile = new ABC.Doc();
			if (Dimok.Images.Enums.GetPageType(file) != Dimok.Images.PageTypes.PDF)
				file = TiffToPDF(file);
			docFile.Read(file);
			List<byte[]> pages = new List<byte[]>();
			foreach (int page in FileManager.MakePageNumbers(docFile.PageCount,pageNumbers) ) {
				ABC.Doc docPage = new ABC.Doc();
				docFile.PageNumber = page;
				docPage.MediaBox.String = docFile.MediaBox.String;
				docPage.Rect.String = docPage.MediaBox.String;
				docPage.AddImageDoc(docFile, page, null);
				pages.Add(docPage.GetData());
				docPage.Clear();
			}
			return pages.ToArray();
		}
	}
}
